//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-03-12     ZhangYihua   first version
//
// Description  : 
//################################################################################

// same sample rate fraction delay filter
module ssr_fdf #(
parameter           PHS_FW                  = 11,
parameter           NCO_FW                  = 16,   // must NCO_FW >= PHS_FW
parameter           RDPL                    = 2,    // RAM read pipeline
parameter           CDW                     = 14,   // coefficient data width
parameter           CFW                     = 13,   // coefficient fractional width
parameter           IDW                     = 12,   // input data width
parameter           IFW                     = 9,    // input fractional width
parameter           ODW                     = 12,   // output data width
parameter           OFW                     = 9,    // output fractional width
parameter           COE_NUM                 = 10,   // COE_NUM>=1
parameter           IN_REG_EN               = 1'b0,
parameter           CSCD_LEN_MAX            = 4,    // CSCD_LEN_MAX>=2, cascade length, if too bigger, bad Fmax
parameter           CSCD_GRP                = COE_NUM/CSCD_LEN_MAX + (((COE_NUM%CSCD_LEN_MAX)==0) ? 0 : 1),
parameter           CSCD_LEN                = COE_NUM/CSCD_GRP + (((COE_NUM%CSCD_GRP)==0) ? 0 : 1),
parameter           LVL_NUM                 = (CSCD_GRP<=1) ? 1 : $clog2(CSCD_GRP),
parameter [LVL_NUM-1:0]     LVL_REG         = {LVL_NUM{1'b0}},  // specify register for each level

// the following parameters are calculated automatically
parameter           FREQ_DW                 = NCO_FW+1
) ( 
input                                       rst_n,
input                                       clk,

input                                       ref_vld,        // reference valid for sample rate
input               [FREQ_DW-1:0]           freq_word,      // freq_word is more of less than (2^NCO_FW)

output              [PHS_FW-1:0]            coe_ram_radr,
output                                      coe_ram_ren,
input               [COE_NUM*CDW-1:0]       coe_ram_rdat,   // {CN, ...... , C1, C0}, C0 is for latest data

output                                      smp_fifo_ren,
input               [2*IDW-1:0]             smp_fifo_rdat,  // {smp1, smp0}, smp1 is later than smp0

output                                      out_vld,
output      signed  [ODW-1:0]               out_dat
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam          PIPE_NUM                = COE_NUM;

wire                [2-1:0]                 shift;
wire                [PHS_FW-1:0]            phase;
reg                                         vld_dly  [RDPL:0];
reg                 [2-1:0]                 shift_dly[RDPL:0];
reg                 [PHS_FW-1:0]            phase_dly;
reg                                         empty_dly[RDPL:0];
reg                                         buf_empty;
reg                 [IDW-1:0]               buf_dat;
reg                 [PIPE_NUM*IDW-1:0]      pipe_dat;

//################################################################################
// main
//################################################################################

nco #(
        .PHS_FW                         (PHS_FW                         ),
        .NCO_FW                         (NCO_FW                         ),
        .FREQ_DW                        (FREQ_DW                        ),	// FREQ_DW<=NCO_FW or FREQ_DW>NCO_FW is both OK
        .RND_EN                         (1'b0                           ) 	// 1'b0:truncate lower bits directly; 1'b1:rounding
) u_nco ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),
        .cke                            (ref_vld                        ),

        .freq_word                      (freq_word                      ),	// output frequency is F_clk*freq_word/(2^NCO_FW)

        .over_1p                        (shift                          ),	// combinational logic output
        .phase                          (phase                          )	// it's maybe early than acc_over if RDN_EN
);

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        vld_dly[0]   <=`U_DLY 1'b0;
        shift_dly[0] <=`U_DLY 2'b0;
        phase_dly    <=`U_DLY {PHS_FW{1'b0}};
    end else begin
        vld_dly[0]   <=`U_DLY ref_vld;
        shift_dly[0] <=`U_DLY shift;
        phase_dly    <=`U_DLY phase;
    end
end

// shift
// 2'b00:keep pipe_dat unshift
// 2'b01:shift pipe_dat with one sample
// 2'b10:shift pipe_dat with two samples
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        buf_empty <=`U_DLY 1'b1;
    end else if (vld_dly[0]==1'b1) begin
        if (shift_dly[0]==2'b01)
            buf_empty <=`U_DLY ~buf_empty;
        else
            ;
    end
end

always@(*) begin
    empty_dly[0] = buf_empty;
end

assign coe_ram_radr = phase_dly;
assign coe_ram_ren = vld_dly[0];

assign smp_fifo_ren = (vld_dly[0]==1'b0)    ? 1'b0 :
                      (shift_dly[0]==2'b00) ? 1'b0 :
                      (shift_dly[0]>=2'b10) ? 1'b1 : 
                                              empty_dly[0];

genvar g0;
generate for (g0=0; g0<RDPL; g0=g0+1) begin:G_RDPL
    
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            vld_dly[g0+1]   <=`U_DLY 1'b0;
            empty_dly[g0+1] <=`U_DLY 1'b1;
            shift_dly[g0+1] <=`U_DLY 2'b0;
        end else begin
            vld_dly[g0+1]   <=`U_DLY vld_dly[g0];
            empty_dly[g0+1] <=`U_DLY empty_dly[g0];
            shift_dly[g0+1] <=`U_DLY shift_dly[g0];
        end
    end

end endgenerate

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        buf_dat <=`U_DLY {IDW{1'b0}};
    end else if (vld_dly[RDPL]==1'b1) begin
        if (empty_dly[RDPL]==1'b1 && shift_dly[RDPL]==2'b01)
            buf_dat <=`U_DLY smp_fifo_rdat[1*IDW+:IDW];
        else if (empty_dly[RDPL]==1'b0 && shift_dly[RDPL]>=2'b10)
            buf_dat <=`U_DLY smp_fifo_rdat[1*IDW+:IDW];
        else
            ;
    end
end

// smp_fifo_rdat = {smp1 , smp0}    (smp1 is later than smp0)
//                   ___    ___ 
//                  |_1_|  |_0_|
//                    |      |     x(n)   x(n-1)             x(n-N)
//                   \|/    \|/    ___     ___                 ___ 
// pipe_dat           ----------->|_0_|-->|_1_|-->  .....  -->|_N_|
//                      /|\         |       |                   |
//                       |      C0->*   C1->*       .....   CN->*
//                      \|/         |       |                   |
//                      ---         ------->+------> ....  ---->+----> y(n)
// buf_dat             |_B_|

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        pipe_dat[0*IDW+:IDW] <=`U_DLY {IDW{1'b0}};
    end else if (vld_dly[RDPL]==1'b1) begin
        if (empty_dly[RDPL]==1'b0 && shift_dly[RDPL]==2'b01)
            pipe_dat[0*IDW+:IDW] <=`U_DLY buf_dat;
        else if (empty_dly[RDPL]==1'b1 && shift_dly[RDPL]>=2'b10)
            pipe_dat[0*IDW+:IDW] <=`U_DLY smp_fifo_rdat[1*IDW+:IDW];
        else if (shift_dly[RDPL]>=2'b01)
            pipe_dat[0*IDW+:IDW] <=`U_DLY smp_fifo_rdat[0*IDW+:IDW];
        else
            ;
    end
end

genvar g1;
generate 
if (PIPE_NUM>=2) begin:G_P1
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            pipe_dat[1*IDW+:IDW] <=`U_DLY {IDW{1'b0}};
        end else if (vld_dly[RDPL]==1'b1) begin
            if (shift_dly[RDPL]==2'b01)
                pipe_dat[1*IDW+:IDW] <=`U_DLY pipe_dat[0*IDW+:IDW];
            else if (empty_dly[RDPL]==1'b0 && shift_dly[RDPL]>=2'b10)
                pipe_dat[1*IDW+:IDW] <=`U_DLY buf_dat;
            else if (empty_dly[RDPL]==1'b1 && shift_dly[RDPL]>=2'b10)
                pipe_dat[1*IDW+:IDW] <=`U_DLY smp_fifo_rdat[0*IDW+:IDW];
            else
                ;
        end
    end
end

for (g1=2; g1<PIPE_NUM; g1=g1+1) begin:G_PN
    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            pipe_dat[g1*IDW+:IDW] <=`U_DLY {IDW{1'b0}};
        end else if (vld_dly[RDPL]==1'b1) begin
            if (shift_dly[RDPL]==2'b01)
                pipe_dat[g1*IDW+:IDW] <=`U_DLY pipe_dat[(g1-1)*IDW+:IDW];
            else if (shift_dly[RDPL]>=2'b10)
                pipe_dat[g1*IDW+:IDW] <=`U_DLY pipe_dat[(g1-2)*IDW+:IDW];
            else
                ;
        end
    end
end 
endgenerate

sum_of_prdct #(     // sum of all products
        .CDW                            (CDW                            ),	// coefficient data width
        .CFW                            (CFW                            ),	// coefficient fractional width
        .IDW                            (IDW                            ),	// input data width
        .IFW                            (IFW                            ),	// input fractional width
        .ODW                            (ODW                            ),	// output data width
        .OFW                            (OFW                            ),	// output fractional width
        .COE_NUM                        (COE_NUM                        ),	// COE_NUM>=1
        .IN_REG_EN                      (IN_REG_EN                      ),
        .CSCD_LEN_MAX                   (CSCD_LEN_MAX                   ),	// CSCD_LEN_MAX>=2, cascade length, if too bigger, bad Fmax
        .CSCD_GRP                       (CSCD_GRP                       ),
        .CSCD_LEN                       (CSCD_LEN                       ),
        .LVL_NUM                        (LVL_NUM                        ),
        .LVL_REG                        (LVL_REG                        )	// specify register for each level
) u_sum_of_prdct ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .in_vld                         (vld_dly[RDPL]                  ),
        .in_coe                         (coe_ram_rdat                   ),	// coefficients, {CN-1, ......, c1, c0}, c0 is far away from center
        .in_dat                         (pipe_dat                       ),	// datas {x(n-N+1), ......  x(n-1), x(n)}, DLY of x(n-1) > x(n)

        .out_vld                        (out_vld                        ),
        .out_dat                        (out_dat                        ),	// y(n)
        .over                           (                               )
);

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
